package base;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

// Representa uma possível solução para o Problema do Caixeiro Viajante.
// Em um algoritmo genético, corresponde a um cromossomo.
public class Solucao implements Comparable<Solucao> {

    private final List<Vertice> caminho;// a lista de vértices que corresponde ao percurso completo de uma solução.
    private double custo = 0;//comprimento total do percurso/caminho

    public Solucao() {
        caminho = new ArrayList<>();
    }

    public Solucao(List<Vertice> caminho) {
        this.caminho = caminho;

        for (int i = 0; i < caminho.size() - 1; i++) {
            custo += caminho.get(i).distanciaAte(caminho.get(i + 1));
        }

        fechaCiclo();
    }

    // Adiciona ao custo a distância entre o último vértice e o primeiro, já que
    // é necessário voltar ao vértice inicial após visitar todos.
    private void fechaCiclo() {
        Vertice ultimo = caminho.get(caminho.size() - 1);
        Vertice primeiro = caminho.get(0);
        custo += ultimo.distanciaAte(primeiro);
    }

    public double getCusto() {
        return custo;
    }

    public List<Vertice> getCaminho() {
        return caminho;
    }

    // Retorna a quantidade de vértices do caminho.
    public int tamanho() {
        return caminho.size();
    }

    // Inverte, no caminho, os vértices de 2 posições.
    // É usado na mutação.
    public void inverte(int pos1, int pos2) {
        Vertice v1 = caminho.get(pos1);
        Vertice v2 = caminho.get(pos2);
        
        caminho.remove(pos1);
        caminho.add(pos1, v2);

        caminho.remove(pos2);
        caminho.add(pos2, v1);
    }

    // Deve ser chamada apenas para as soluções filhas
    public boolean sorteadaParaMutacao(int chance) {
        return Aleatorio.geraEntre(0, 100) < chance;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("Percurso:\n");

        for (int i = 0; i < caminho.size(); i++) {
            sb.append(caminho.get(i).toString()).append(" --> ");

            if ((i + 1) % 5 == 0) {
                sb.append("\n");
            }
        }

        sb.append(caminho.get(0)).append("\n\n");
        NumberFormat nf = new DecimalFormat("0,000.00");
        sb.append("Custo = ").append(nf.format(custo)).append("\n");

        return sb.toString();
    }

    // Uma solução é "menor" que outra quando seu custo total é menor.
    // É usado na seleção de cromossomos para reprodução.
    @Override
    public int compareTo(Solucao s) {
        double difCusto = this.custo - s.custo;
        
        return difCusto > 0 ? 1 : difCusto < 0 ? -1 : 0;
    }

    // 2 soluções são iguais quando cada vértice correspondente de seu caminho é
    // igual.
    @Override
    public boolean equals(Object o) {
        if (o instanceof Solucao) {
            Solucao s = (Solucao) o;

            for (int i = 0; i < s.tamanho(); i++) {
                if (!s.caminho.get(i).equals(this.caminho.get(i))) {
                    return false;
                }
            }

            return true;
        }

        return false;
    }
}
